package judge

import (
	"JudgeSandBox/cgroup"
	"JudgeSandBox/common"
	"JudgeSandBox/utils"
	"bytes"
	"encoding/json"
	"os"
	"os/exec"
	"path"
	"syscall"
)

type Java struct {
	Impl
}

func (j Java) Compile(request common.JudgeRequest) error {
	tmpUuidPath := path.Join(common.TmpPath, request.Uuid)
	//生成文件Main.java并写入源代码 // /root/judge/tmp/xxxx/mnt/Main.java
	mainJavaFilePath := path.Join(tmpUuidPath, "mnt", "Main.java")
	if file, err := os.Create(mainJavaFilePath); err != nil {
		return err
	} else if _, err := file.WriteString(request.SourceCode); err != nil {
		return err
	}
	//编译成字节码 /java/jdk-11/bin/javac /Main.java -s /
	//使用chroot
	//获取编译错误信息
	output, compileError := exec.Command("chroot", path.Join(tmpUuidPath, "mnt"), common.Javac, "/Main.java", "-s", "/").CombinedOutput()
	if err := compileError; err != nil {
		return CompileError{
			msg: string(output),
		}
	}
	return nil
}

func (j Java) Execute(request common.JudgeRequest) (*common.JudgeResponse, error) {
	//mntPath := path.Join(common.TmpPath, request.Uuid, "mnt")
	//cmd := exec.Command("chroot", mntPath, "/proc/self/exe", "call", common.Java, "/Main")
	cmd := exec.Command("/proc/self/exe", "call", common.Java, "Main")
	stdinPipe, _ := cmd.StdinPipe() //管道通信
	//stdoutPipe, _ := cmd.StdoutPipe() //子进程关闭时，管道也会随之关闭
	//stderrPipe, _ := cmd.StderrPipe()
	outBuf := bytes.NewBuffer(make([]byte, 0))
	errBuf := bytes.NewBuffer(make([]byte, 0))
	cmd.Stdout = outBuf
	cmd.Stderr = errBuf
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
			syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
	}
	var cpu *cgroup.Cpu
	var memory *cgroup.Memory
	if err := cmd.Start(); err == nil {
		//将子进程加入cgroup
		cpu = cgroup.GetCpu(request.Uuid)
		memory = cgroup.GetMemory(request.Uuid)

		/*if err := cpu.Set(common.CpuDefaultQuota, common.CpuDefaultPeriod); err != nil {
			//默认配额
			utils.PrintToLog("cpu设置配额出错", err)
		}
		if err := cpu.Put(cmd.Process.Pid); err != nil {
			//子进程加入cpu cgroup
			utils.PrintToLog("子进程加入cpu cgroup出错", err)
		}*/
		if err := memory.Set(request.MaxMemoryLimit); err != nil {
			//限制最大内存
			utils.PrintToLog("限制最大内存出错", err)
		}
		if err := memory.Put(cmd.Process.Pid); err != nil {
			//子进程加入memory cgroup
			utils.PrintToLog("子进程加入memory cgroup出错", err)
		}

		//往call进程写入评判请求数据
		marshal, _ := json.Marshal(request)
		stdinPipe.Write(marshal)
	} else {
		utils.PrintToLog("java 执行出错", err)
		//return nil, err
	}
	cmd.Wait()
	//子进程结束，清理子进程cgroup
	if err := cpu.Remove(); err != nil {
		utils.PrintToLog("清理cpu cgroup出错", err)
	}
	if err := memory.Remove(); err != nil {
		utils.PrintToLog("清理memory cgroup出错", err)
	}

	if errBuf.Len() > 0 {
		utils.PrintToLog("java 子进程错误", errBuf.String())
	}
	if outBuf.Len() > 0 {
		//返回测评结果
		response := common.JudgeResponse{}
		json.Unmarshal(outBuf.Bytes(), &response)
		utils.PrintToLog("java 从子进程返回的测评结果：", response)
		return &response, nil
	}
	utils.PrintToLog("Java 返回结果字节数组长度为0")
	return nil, nil
}
